home *** CD-ROM | disk | FTP | other *** search
/ Pascal Super Library / Pascal Super Library (CW International)(1997).bin / TURB_VIS / MACROS / MACROS.PAS < prev    next >
Pascal/Delphi Source File  |  1992-01-31  |  20KB  |  701 lines

  1. unit Macros;
  2.  
  3. (***************************************************************************)
  4. (*                          Turbo Vision Macros                            *)
  5. (*                       $25 Shareware, Version 1.0                        *)
  6. (*                                                                         *)
  7. (*            (c) Copyright 1992 Cybersoft & Lawrence V. Koepke            *)
  8. (*                          All Rights Reserved                            *)
  9. (*                                                                         *)
  10. (*                               Cybersoft                                 *)
  11. (*                   1921 Minto Dr., San Jose, CA 95132                    *)
  12. (*                             (408) 272-2927                              *)
  13. (***************************************************************************)
  14.  
  15. {$X+}
  16. {.$DEFINE TESTING}
  17.  
  18. {
  19. Turbo Vision Macros is a complete Event Macro Handler, and is released as
  20. Shareware. If you find it useful, please support my efforts. I am an
  21. independant developer. I have no job... I support the wife and kids with my
  22. wits and perseverance to succeed. If enough people support my efforts with
  23. TV Macros, I may even release future versions that support advanced features
  24. (i.e. Loops, If-Then, etc.)! I encourage you to pass this code on to your
  25. friends and colleagues, for, if nothing else, I'm sure that it will make some
  26. people's jobs easier; but, please, do not pass on modified code... let's
  27. have some version control here, send me your modifications.
  28.  
  29. This unit Replaces TApp.GetEvent with a method having the following features:
  30. - Records events as macros
  31. - Plays back events in macros
  32. - Both Mouse and Keyboard supported
  33. - Macros can record the playback of other macros
  34. - Macros are selected from a pick-list for playback
  35. - Macros are given a name up to 50 characters long
  36. - Halt playback with Escape and confirmation (requires MsgBox)
  37. - Adds app. 16K to the application (with integrated debugger information)
  38. - All non-current macros are kept on disk, macros are only loaded when run
  39. - Does NOT replace BIOS keyboard interrupt 16 or 9
  40. - Macros are stored in two files : MACROS.NDX and MACROS.MAC.
  41.  
  42. Macros requires units Lists and Picks (also from Cybersoft) to compile.
  43. }
  44.  
  45. interface
  46. uses App, Drivers, Picks, Lists, StdDlg, Objects, Dialogs;
  47.  
  48. type
  49.      (*-------- The basic App type to include macros ------------------*)
  50.  
  51.      PMacApp = ^TMacApp;
  52.      TMacApp = object (TApplication)
  53.        constructor Init;
  54.        destructor Done; virtual;
  55.        procedure GetEvent (var Event : TEvent); virtual;
  56.      end;
  57.  
  58.      (*-------- The Macro Dialog (replaces event handler) ----------*)
  59.  
  60.      PMacDialog = ^TMacDialog;
  61.      TMacDialog = object (TDialog)
  62.        procedure HandleEvent (var Event : TEvent); virtual;
  63.      end;
  64.  
  65.      (*-------- Macro file record ----------------------------------*)
  66.  
  67.      AMacroRecord = TEvent;                  {used to define file record}
  68.  
  69.  
  70.      (*-------- Macro Index file record ----------------------------*)
  71.  
  72.      AMacroIndex = Record
  73.                      Name   : String [50];
  74.                      Start,
  75.                      Length : Integer;
  76.                    end;
  77.  
  78.  
  79.      (*-------- The Macro ------------------------------------------*)
  80.      { Each macro is a collection of Events of type TEvent. }
  81.  
  82.      PMacro = ^TMacro;
  83.      TMacro = object (TQueue)
  84.      end;
  85.  
  86.  
  87.      (*------- A stack of macros. ----------------------------------*)
  88.      { Used to store interrupted macros (ones that call
  89.        other macros. (A Stack of Queues, so to speak.) }
  90.  
  91.      PMacroStack = ^TMacroStack;
  92.      TMacroStack = object(TStack)
  93.        procedure PushMacro (Macro : PMacro);
  94.      end;
  95.  
  96.      (*------- The macro picklist ----------------------------------*)
  97.  
  98.      (* - - - - - - - - -- - - - - - -- - - - - *)
  99.  
  100.      { Used for Sorted macro list. }
  101.      TSortRecord = record
  102.        Name : String [50];
  103.        RecNUm : integer;
  104.      end;
  105.  
  106.      PMacroList = ^TMacroList;
  107.      TMacroList = object (TSortedCollection)
  108.        function Compare (Key1, Key2 : Pointer): Integer; virtual;
  109.        procedure FreeItem (Item : Pointer); virtual;
  110.      end;
  111.  
  112.      PMacroListBox = ^TMacroListBox;
  113.      TMacroListBox = object (TSortedListBox)        {from StdDlg}
  114.        procedure HandleEvent (var Event : TEvent); virtual;
  115.        function GetText (Item : Integer; MaxLen : Integer) : String; virtual;
  116.      end;
  117.  
  118.      (*-------------------------------------------------------------*)
  119.      PEvent = ^TEvent;
  120.  
  121.  
  122. procedure StartRecording;
  123. procedure StopRecording;
  124. procedure StartPlayback;
  125. procedure StopPlayback;
  126. procedure DeleteMacro; {Can this be disabled during Recording or playback?}
  127.  
  128. implementation
  129. uses Views, Strings, Crt, MsgBox;
  130.  
  131. type PSortRecord = ^TSortRecord;
  132.  
  133. var
  134.    MacroFile          : file of AMacroRecord;      {file of macros}
  135.    MacroFileIndex     : file of AMacroIndex;       {file of indexes to macros}
  136.    MacFileName        : string;                    {file name root; no ext.}
  137.    RecordMacIndex     : AMacroIndex;               {1 index record}
  138.    MacPickList        : PPickList;                 {picklist of macros}
  139.    MacStack           : PMacroStack;               {collection of macros}
  140.    InRecording,
  141.    InPlayback         : boolean;                   {states}
  142.    PtrEvent           : PEvent;                    {used only in GetEvent}
  143.    OurMacro           : PMacro;                    {the current macro}
  144.    CheckHalt          : boolean;                   {allows macro interruption}
  145.  
  146.  
  147.  
  148. (* ------------------------- The Macro Files ---------------------------- *)
  149.  
  150. function OpenMacroFiles (Filename : string): boolean;
  151. var ok : boolean;
  152. begin
  153.   ok := false;
  154. {$I-}
  155.   Assign (MacroFile, Filename + '.MAC');
  156.   Reset (MacroFile);
  157.   ok := IOResult = 0;
  158.   if not ok then
  159.   begin
  160.     Rewrite (MacroFile);
  161.     ok := IOResult = 0;
  162.     if not ok then
  163.       MessageBox('Couldn''t open or create macro data file.',
  164.                  nil, mfOKButton);
  165.   end;
  166.  
  167.   if ok then
  168.   begin
  169.     Assign (MacroFileIndex, Filename + '.NDX');
  170.     Reset (MacroFileIndex);
  171.     ok := IOResult = 0;
  172.     if not ok then
  173.     begin
  174.       Rewrite (MacroFileIndex);
  175.       ok := IOResult = 0;
  176.       if not ok then
  177.         MessageBox('Couldn''t open or create macro index file.',
  178.                    nil, mfOKButton);
  179.     end;
  180.   end;
  181.  
  182.   OpenMacroFiles := ok;
  183. {$I+}
  184. end;
  185.  
  186.  
  187. procedure CloseMacrofiles;
  188. begin
  189.   Close (MacroFile);
  190.   Close (MacroFileIndex);
  191. end;
  192.  
  193.  
  194.  
  195. (* ------------------------ The Macro Dialog Box ------------------------- *)
  196.  
  197. {This HandleEvent replaces the space with an underscore because StdDlg's
  198.  TSortedListBox does not recognize spaces with alphanumeric searches for
  199.  the list items. This HandleEvent also converts characters to upper-case,
  200.  since TSortedListBox is case-sensitive.}
  201. procedure TMacDialog.HandleEvent (var Event : TEvent);
  202. begin
  203.   if Event.What = evKeyDown then
  204.     if Event.CharCode = #32 then
  205.       Event.CharCode := #95
  206.     else
  207.       Event.CharCode := UpCase(Event.CharCode);
  208.   TDialog.HandleEvent (Event);
  209. end;
  210.  
  211.  
  212. FUNCTION MakeDialog : PMacDialog;
  213. var
  214.   Dlg : PMacDialog;
  215.   R : TRect;
  216.   Control, Labl, Histry : PView;
  217. Begin
  218. R.Assign(4,6,76,13);
  219. New(Dlg, Init(R, 'Macro'));
  220.  
  221. R.Assign(17,2,69,3);
  222. Control := New(PInputLine, Init(R, 50));
  223. Dlg^.Insert(Control);
  224.  
  225.   R.Assign(2,2,17,3);
  226.   Labl := New(PLabel, Init(R, 'Macro Name  : ', Control));
  227.   Dlg^.Insert(Labl);
  228.  
  229. R.Assign(46,4,54,6);
  230. Control := New(PButton, Init(R, ' OK ', cmOK, bfDefault));
  231. Dlg^.Insert(Control);
  232.  
  233. R.Assign(57,4,67,6);
  234. Control := New(PButton, Init(R, 'Cancel', cmCancel, bfNormal));
  235. Dlg^.Insert(Control);
  236.  
  237. Dlg^.SelectNext(False);
  238. MakeDialog := Dlg;
  239. end;
  240.  
  241. var
  242.   DataRec : record
  243.     Name : String[50]; {Inputline}
  244.     end;
  245.  
  246.  
  247.  
  248. (* ---------------------------- MacroStack ------------------------------- *)
  249.  
  250. procedure TMacroStack.PushMacro (Macro : PMacro);
  251. var P : PMacro;
  252. begin
  253.   new (P);
  254.   P := Macro;
  255.   Push(P);
  256. end;
  257.  
  258. (* --------------------------- Macro PickList stuff -----------------------*)
  259.  
  260. { - - - - - - - - - - - - - - - TMacroList - - - - - - - - - - - - - - - - }
  261. { This is the PSortedCollection descendant that is inserted into the dialog. }
  262.  
  263. function TMacroList.Compare (Key1, Key2 : Pointer): Integer;
  264. begin
  265.   if PSortRecord(Key1)^.Name = PSortRecord(Key2)^.Name then Compare := 0
  266.   else if PSortRecord(Key1)^.Name > PSortRecord(Key2)^.Name then Compare := 1
  267.   else Compare := -1;
  268. end;
  269.  
  270. procedure TMacroList.FreeItem (Item : Pointer);
  271. begin
  272.   dispose (PSortRecord(Item));
  273. end;
  274.  
  275.  
  276. { - - - - - - - - - - - - - - - TMacroListBox - - - - - - - - - - - - - - - }
  277.  
  278. { The TSortedListbox descendant that is inserted into the dialog. }
  279.  
  280. { HandleEvent Converts the space character to the underscore, since
  281.   TSortedListBox does not recognize the underscore. This HandleEvent also
  282.   converts characters to upper-case, since TSortedListBox is case-sensitive.}
  283.  
  284. procedure TMacroListBox.HandleEvent (var Event : TEvent);
  285. begin
  286.   if Event.What = evKeyDown then
  287.     if Event.CharCode = #32 then
  288.       Event.CharCode := #95
  289.     else
  290.       Event.CharCode := UpCase(Event.CharCode);
  291.   TSortedListBox.HandleEvent (Event);
  292. end;
  293.  
  294.  
  295. { GetText gets the name from the record. }
  296.  
  297. function TMacroListBox.GetText (Item : Integer; MaxLen : Integer) : String;
  298. var SR : PSortRecord;
  299. begin
  300.   SR := PSortRecord(List^.At(Item));
  301.   GetText := SR^.Name;
  302. end;
  303.  
  304.  
  305. { - - - - - - - - - - - - - - - - Build sorted list - - - - - - - - - - - - }
  306.  
  307. { BuildSortedList builds the sorted list that is inserted in the dialog. }
  308.  
  309. function BuildSortedList (var List : PMacroList): boolean;
  310. var
  311.     MacroFilePos,
  312.     MacroFileIndexPos : Integer;
  313.     PlaybackMacIndex : AMacroIndex;
  314.     OurSortRecord : TSortRecord;
  315.     i : integer;
  316.  
  317.  
  318.     { NewRecord creates a pointer and allocates space for the SortRecord. }
  319.  
  320.     function NewRecord (ASortRecord : TSortRecord): Pointer;
  321.     var P : PSortRecord;
  322.     begin
  323.       new (P);
  324.       P^ := ASortRecord;
  325.       NewRecord := P;
  326.     end;
  327.  
  328. begin
  329.   if InRecording then
  330.   begin
  331.     MacroFilePos := FilePos (MacroFile);
  332.     MacroFileIndexPos := FilePos (MacroFileIndex);
  333.   end
  334.   else
  335.   if not OpenMacroFiles (MacFilename) then
  336.   begin
  337.     MessageBox ('Build List problem.', nil, mfOKButton);
  338.     exit;
  339.   end;
  340.  
  341.   BuildSortedList := true;
  342.   List := New(PMacroList,Init(100, 100));
  343.   Seek (MacroFileIndex, 0);
  344.   i := 0;
  345.   while not EOF (MacroFileIndex) do begin
  346.     Read (MacroFileIndex, PlaybackMacIndex);
  347.     OurSortRecord.Name := PlaybackMacIndex.Name;
  348.     OurSortRecord.RecNum := i;
  349.     List^.Insert(NewRecord(OurSortRecord));
  350.     Inc(i);
  351.   end;
  352.  
  353.   if InRecording then
  354.   begin
  355.     Seek (MacroFile, MacroFilePos);
  356.     Seek (MacroFileIndex, MacroFileIndexPos );
  357.   end
  358.   else
  359.     CloseMacroFiles;
  360. end;
  361.  
  362. { - - - - - - - - - - - - - - Pick a Macro - - - - - - - - - - - - - - - - }
  363.  
  364. function PickMacro (var which : integer) : boolean;
  365. var
  366.     OurList           : PMacroList;
  367.     OurRecord         : TSortRecord;
  368.     ListBox           : PMacroListBox;
  369.     OurScroller       : PView;
  370.     ItemNum           : Integer;
  371.  
  372. begin
  373.    PickMacro := false;
  374.    New(MacPickList, Init(9,3,70,17));
  375.    {New(MacPickList, Init(6,3,73,21));}
  376.    OurScroller := New(PScrollbar, Init(ScrollBarPRect^));
  377.    ListBox := New(PMacroListBox, Init(ListBoxPRect^, 1, PScrollbar(OurScroller)));
  378.    BuildSortedList (OurList);
  379.    if MacPickList^.ListItemPicked(OurScroller, ListBox, OurList,
  380.                                                'Macros', ItemNum) then
  381.    begin
  382.      PickMacro := true;
  383.      OurRecord := PSortRecord(OurList^.At(ItemNum))^;
  384.      which := OurRecord.RecNum;
  385.    end;
  386.    Dispose (OurList, Done);
  387.    OurList := nil;
  388.    Dispose (MacPickList, Done);
  389. end;
  390.  
  391. (* ---------------------------- Recording ---------------------------------*)
  392.  
  393. procedure StartRecording;
  394. var D : PDialog;
  395.     cmd : word;
  396. begin
  397.   D := MakeDialog;
  398.   cmd := Desktop^.ExecView (D);
  399.   if cmd = cmOK then
  400.   begin
  401.     D^.GetData(DataRec);
  402.     RecordMacIndex.Name := DataRec.Name;
  403.     RecordMacIndex.Length := 0;
  404.     if not OpenMacroFiles (MacFileName) then exit;
  405.     Seek (MacroFile, FileSize(MacroFile));
  406.     RecordMacIndex.Start := FileSize(MacroFile);
  407.     InRecording := true
  408.   end;
  409.   Dispose (D, Done);
  410. end;
  411.  
  412.  
  413. procedure StopRecording;
  414. begin
  415.   if not InRecording then exit;
  416.   if InPlayback then exit;
  417.   Seek (MacroFileIndex, FileSize(MacroFileIndex));
  418.   Write (MacroFileIndex, RecordMacIndex);
  419.   CloseMacroFiles;
  420.   InRecording := false
  421. end;
  422.  
  423.  
  424.  
  425.  
  426. (* ------------------------------ Playback --------------------------------*)
  427.  
  428. {NewEvent creates new pointer for a macro event, much like NewStr does. }
  429.  
  430. function NewEvent (Event : TEvent) : Pointer;
  431. var PtrEvent : PEvent;
  432. begin
  433.   new (PtrEvent);
  434.   PtrEvent^ := Event;
  435.   NewEvent := PtrEvent;
  436. end;
  437.  
  438.  
  439. procedure StartPlayback;
  440. var ItemNum, i        : Integer;
  441.     OurEvent          : TEvent;
  442.     MacroFilePos,
  443.     MacroFileIndexPos : Integer;
  444.     MacroIndexRec     : AMacroIndex;
  445.  
  446. begin
  447.    if PickMacro (ItemNum) then
  448.    begin
  449.      if InRecording then
  450.      begin
  451.        MacroFilePos := FilePos (MacroFile);
  452.        MacroFileIndexPos := FilePos (MacroFileIndex);
  453.      end
  454.      else
  455.      if not OpenMacroFiles (MacFilename) then
  456.      begin
  457.        Dispose (MacPickList, Done);
  458.        exit;
  459.      end;
  460.  
  461.      if OurMacro <> nil then
  462.        MacStack^.Push (OurMacro);
  463.      new (OurMacro, Init (SizeOf (TEvent)));
  464.      Seek (MacroFileIndex, ItemNum);
  465.      Read (MacroFileIndex, MacroIndexRec);
  466.  
  467.      {Build macro collection}
  468.      for i := MacroIndexRec.Start to
  469.              (MacroIndexRec.Start + MacroIndexRec.length - 1) do
  470.      begin
  471.        Seek (MacroFile, i);
  472.        Read (MacroFile, OurEvent);
  473.        OurMacro^.Insert (NewEvent(OurEvent));
  474.      end;
  475.  
  476.      if InRecording then
  477.      begin
  478.        Seek (MacroFile, MacroFilePos);
  479.        Seek (MacroFileIndex, MacroFileIndexPos );
  480.      end
  481.      else
  482.        CloseMacroFiles;
  483.  
  484.      InPlayback := true;
  485.    end;
  486. end;
  487.  
  488. procedure StopPlayback;
  489. begin
  490.   if MacStack^.NotEmpty then
  491.     OurMacro := MacStack^.Pop
  492.   else
  493.     InPlayback := false;
  494. end;
  495.  
  496. (* -------------------------- Delete Macro -------------------------- *)
  497.  
  498. procedure DeleteMacro;
  499. var MacroNum : Integer;
  500.     cmd      : word;
  501.     TempMacroFile      : file of AMacroRecord;      {file of macros}
  502.     TempMacroFileIndex : file of AMacroIndex;       {file of indexes to macros}
  503.     i, j     : Integer;
  504.     IndexRec : AMacroIndex;
  505.     Length   : integer;
  506.     AnEvent  : TEvent;
  507.  
  508.     function CheckIO : boolean;
  509.     var ok : boolean;
  510.     begin
  511.       ok := IOResult = 0;
  512.       if not ok then
  513.         MessageBox ('File I/O failure with Delete operation.', nil,
  514.                      mfOKButton);
  515.       CheckIO := ok;
  516.     end;
  517.  
  518.     function IOok : boolean;
  519.     var ok : boolean;
  520.     begin
  521.       ok := CheckIO;
  522.       if not OK then
  523.       begin
  524.         CloseMacroFiles;
  525.         Close (TempMacroFile);
  526.         Close (TempMacroFileIndex);
  527.       end;
  528.     end;
  529.  
  530.  
  531. begin
  532.   if InRecording or InPlayback then exit;
  533.   if PickMacro (MacroNum) then
  534.   begin
  535.     cmd := MessageBox ('Really delete macro?', nil, mfYesNoCancel);
  536.     if cmd = cmYes then
  537.     begin
  538.       if OpenMacroFiles (MacFileName) then
  539.       begin                                         {Create temporary files }
  540.         Assign (TempMacroFile, 'TEMP.MAC');         {to copy macros into.   }
  541.         Assign (TempMacroFileIndex, 'TEMP.NDX');
  542.         {$I-}
  543.         Rewrite (TempMacroFile);
  544.         if not CheckIO then
  545.         begin
  546.           CloseMacroFiles;
  547.           exit;
  548.         end;
  549.         Rewrite (TempMacroFileIndex);
  550.         if not CheckIO then
  551.         begin
  552.           CloseMacroFiles;
  553.           Close (TempMacroFile);
  554.           exit;
  555.         end;
  556.  
  557.         i := 0; Length := 0;
  558.         While not Eof(MacroFileIndex) do
  559.         begin
  560.           Read (MacroFileIndex, IndexRec);
  561.           if not IOok then exit;
  562.           if i <> MacroNum then
  563.           begin
  564.             IndexRec.Start := IndexRec.Start - Length; {Adjust for deletion.}
  565.             Write (TempMacroFileIndex, IndexRec);      {Copy index record   }
  566.             if not IOok then exit;                     {to temporary file.  }
  567.             for j := 1 to IndexRec.Length do           {Copy macro to the   }
  568.             begin                                      {temporary file.     }
  569.               Read (MacroFile, AnEvent);
  570.               if not IOok then exit;
  571.               Write (TempMacroFile, AnEvent);
  572.               if not IOok then exit;
  573.             end;
  574.           end
  575.           else
  576.           begin
  577.             Length := IndexRec.Length;               {Get deletion adjustment.}
  578.             for j := 1 to IndexRec.Length do         {Move to next macro,   }
  579.             begin                                    {by skipping this one. }
  580.               Read (MacroFile, AnEvent);
  581.               if not IOok then exit;
  582.             end;
  583.             Length := 0;                             {Reset adjustment.     }
  584.           end;
  585.           Inc (i);
  586.         end;
  587.  
  588.         CloseMacroFiles;
  589.         Close (TempMacroFileIndex);
  590.         Close (TempMacroFile);
  591.         Erase (MacroFile);
  592.         Erase (MacroFileIndex);
  593.         Rename (TempMacroFileIndex, MacFileName + '.NDX');
  594.         Rename (TempMacroFile, MacFileName + '.MAC');
  595.       end;
  596.     end;
  597.   end;
  598. end;
  599.  
  600.  
  601. (* -------------------------- MacApp -------------------------------- *)
  602.  
  603. constructor TMacApp.Init;
  604. begin
  605.   TApplication.Init;
  606. end;
  607.  
  608. destructor TMacApp.Done;
  609. begin
  610.   TApplication.Done;
  611. end;
  612.  
  613. procedure TMacApp.GetEvent (var Event : TEvent);
  614. var cmd : word;
  615. Label TheEnd;
  616.  
  617. begin
  618.  
  619.   if CheckHalt or ((not InRecording) and (not InPlayback)) then
  620.     TApplication.GetEvent(Event)
  621.   else
  622.   if InRecording and not InPlayback then
  623.   begin
  624.     TApplication.GetEvent(Event);
  625.     if Event.What <> evNothing then
  626.     begin
  627.       {$I-}
  628.       Write (MacroFile, Event);
  629.       RecordMacIndex.Length := RecordMacIndex.Length + 1;
  630.       if IOResult <> 0 then
  631.       MessageBox ('Couldn''t write event to macro file.', nil, mfOKButton);
  632.       {$I+}
  633.     end;
  634.   end
  635.   else
  636.  
  637.   if InPlayback then
  638.   begin
  639. {$IFDEF TESTING}
  640.     delay (10);       {testing}
  641. {$ENDIF}
  642.  
  643.     {check for macro interrupt with Escape key}
  644.     GetKeyEvent(Event);
  645.     if Event.What and evKeyboard <> 0 then
  646.     begin
  647.       if Event.KeyCode = kbEsc then
  648.       begin
  649.         CheckHalt := true;
  650.         cmd := MessageBox ('Halt playback of macro?', nil, mfYesNoCancel);
  651.         if cmd = cmYes then
  652.         begin
  653.           CheckHalt := false;
  654.           dispose (OurMacro, Done);
  655.           OurMacro := nil;
  656.           while MacStack^.NotEmpty do
  657.           begin
  658.             OurMacro := MacStack^.Pop;
  659.             dispose (OurMacro, Done);
  660.             OurMacro := nil;
  661.           end;
  662.           InPlayback := false;
  663.           ClearEvent (Event);
  664.           PutEvent (Event);
  665.           goto TheEnd;
  666.         end;
  667.         CheckHalt := false;
  668.       end;
  669.     end;
  670.  
  671.     if OurMacro^.NotEmpty then
  672.     begin
  673.       PtrEvent := OurMacro^.Extract;
  674.       Event := PtrEvent^;
  675.       dispose (PtrEvent);
  676.     end
  677.     else
  678.     begin
  679.       dispose (OurMacro, Done);
  680.       OurMacro := nil;
  681.       StopPlayback;
  682.       ClearEvent (Event);
  683.     end;
  684.  
  685.   end;
  686. TheEnd:
  687. end;
  688.  
  689.  
  690.  
  691. begin
  692.   MacFileName := 'MACROS';
  693.   new (MacStack, Init(SizeOf(TMacro)));
  694.   InRecording := false;
  695.   InPlayback  := false;
  696.   new (PtrEvent);
  697.   OurMacro := nil;
  698.   InRecording := false;
  699.   InPlayback := false;
  700.   CheckHalt := false;
  701. end.